# 模拟Chrome浏览器发起HTTP请求的工具类
# 支持GET和POST请求，可设置请求头和token，具有随机间隔功能

import asyncio
import random
import time
import json
from typing import Dict, Any, Optional, Union
import aiohttp
import logging

logger = logging.getLogger(__name__)


class ChromeWebAPI:
    """模拟Chrome浏览器的HTTP请求客户端"""
    
    def __init__(self, base_headers: Optional[Dict[str, str]] = None, default_token: Optional[str] = None):
        """
        初始化Web API客户端
        
        Args:
            base_headers: 基础请求头，默认会使用Chrome的User-Agent
            default_token: 默认的认证token
        """
        self.default_headers = {
            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
            'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
            'Accept-Encoding': 'gzip, deflate, br',
            'Connection': 'keep-alive',
            'Upgrade-Insecure-Requests': '1',
            'Sec-Fetch-Dest': 'document',
            'Sec-Fetch-Mode': 'navigate',
            'Sec-Fetch-Site': 'none',
            'Sec-Fetch-User': '?1',
            'Cache-Control': 'max-age=0'
        }
        
        if base_headers:
            self.default_headers.update(base_headers)
            
        self.default_token = default_token
        self.session = None
        
    async def __aenter__(self):
        """异步上下文管理器入口"""
        connector = aiohttp.TCPConnector(
            limit=100,
            limit_per_host=30,
            ttl_dns_cache=300,
            use_dns_cache=True,
        )
        
        timeout = aiohttp.ClientTimeout(total=30, connect=10)
        
        self.session = aiohttp.ClientSession(
            connector=connector,
            timeout=timeout,
            headers=self.default_headers
        )
        return self
        
    async def __aexit__(self, exc_type, exc_val, exc_tb):
        """异步上下文管理器退出"""
        if self.session:
            await self.session.close()
            
    def _get_random_delay(self, min_delay: float = 1.0, max_delay: float = 3.0) -> float:
        """
        获取随机延迟时间
        
        Args:
            min_delay: 最小延迟时间（秒）
            max_delay: 最大延迟时间（秒）
            
        Returns:
            随机延迟时间
        """
        return random.uniform(min_delay, max_delay)
        
    def _prepare_headers(self, headers: Optional[Dict[str, str]] = None, 
                        token: Optional[str] = None) -> Dict[str, str]:
        """
        准备请求头
        
        Args:
            headers: 自定义请求头
            token: 认证token
            
        Returns:
            合并后的请求头
        """
        final_headers = self.default_headers.copy()
        
        if headers:
            final_headers.update(headers)
            
        # 添加token认证
        token_to_use = token or self.default_token
        if token_to_use:
            final_headers['Authorization'] = f'Bearer {token_to_use}'
            
        return final_headers
        
    async def get(self, url: str, 
                  params: Optional[Dict[str, Any]] = None,
                  headers: Optional[Dict[str, str]] = None,
                  token: Optional[str] = None,
                  delay_range: tuple = (1.0, 3.0),
                  timeout: Optional[int] = None) -> Dict[str, Any]:
        """
        发起GET请求
        
        Args:
            url: 请求URL
            params: URL参数
            headers: 自定义请求头
            token: 认证token
            delay_range: 随机延迟范围 (min, max)
            timeout: 请求超时时间
            
        Returns:
            响应数据字典，包含status_code, headers, text, json等
        """
        if not self.session:
            raise RuntimeError("请在async with语句中使用此方法")
            
        # 随机延迟
        delay = self._get_random_delay(*delay_range)
        logger.info(f"GET请求延迟 {delay:.2f}秒: {url}")
        await asyncio.sleep(delay)
        
        # 准备请求头
        final_headers = self._prepare_headers(headers, token)
        
        try:
            start_time = time.time()
            
            async with self.session.get(
                url, 
                params=params, 
                headers=final_headers,
                timeout=aiohttp.ClientTimeout(total=timeout) if timeout else None
            ) as response:
                
                response_time = time.time() - start_time
                response_text = await response.text()
                
                # 尝试解析JSON
                response_json = None
                try:
                    response_json = await response.json()
                except:
                    pass
                    
                result = {
                    'status_code': response.status,
                    'headers': dict(response.headers),
                    'text': response_text,
                    'json': response_json,
                    'url': str(response.url),
                    'response_time': response_time,
                    'success': 200 <= response.status < 300
                }
                
                logger.info(f"GET请求完成: {url} - 状态码: {response.status} - 响应时间: {response_time:.2f}秒")
                return result
                
        except Exception as e:
            logger.error(f"GET请求失败: {url} - 错误: {e}")
            return {
                'status_code': 0,
                'headers': {},
                'text': '',
                'json': None,
                'url': url,
                'response_time': 0,
                'success': False,
                'error': str(e)
            }
            
    async def post(self, url: str,
                   data: Optional[Union[Dict[str, Any], str]] = None,
                   json_data: Optional[Dict[str, Any]] = None,
                   headers: Optional[Dict[str, str]] = None,
                   token: Optional[str] = None,
                   delay_range: tuple = (1.0, 3.0),
                   timeout: Optional[int] = None) -> Dict[str, Any]:
        """
        发起POST请求
        
        Args:
            url: 请求URL
            data: 表单数据或字符串数据
            json_data: JSON数据
            headers: 自定义请求头
            token: 认证token
            delay_range: 随机延迟范围 (min, max)
            timeout: 请求超时时间
            
        Returns:
            响应数据字典，包含status_code, headers, text, json等
        """
        if not self.session:
            raise RuntimeError("请在async with语句中使用此方法")
            
        # 随机延迟
        delay = self._get_random_delay(*delay_range)
        logger.info(f"POST请求延迟 {delay:.2f}秒: {url}")
        await asyncio.sleep(delay)
        
        # 准备请求头
        final_headers = self._prepare_headers(headers, token)
        
        # 根据数据类型设置Content-Type
        if json_data:
            final_headers['Content-Type'] = 'application/json'
            
        try:
            start_time = time.time()
            
            kwargs = {
                'headers': final_headers,
                'timeout': aiohttp.ClientTimeout(total=timeout) if timeout else None
            }
            
            if json_data:
                kwargs['json'] = json_data
            elif data:
                kwargs['data'] = data
                
            async with self.session.post(url, **kwargs) as response:
                
                response_time = time.time() - start_time
                response_text = await response.text()
                
                # 尝试解析JSON
                response_json = None
                try:
                    response_json = await response.json()
                except:
                    pass
                    
                result = {
                    'status_code': response.status,
                    'headers': dict(response.headers),
                    'text': response_text,
                    'json': response_json,
                    'url': str(response.url),
                    'response_time': response_time,
                    'success': 200 <= response.status < 300
                }
                
                logger.info(f"POST请求完成: {url} - 状态码: {response.status} - 响应时间: {response_time:.2f}秒")
                return result
                
        except Exception as e:
            logger.error(f"POST请求失败: {url} - 错误: {e}")
            return {
                'status_code': 0,
                'headers': {},
                'text': '',
                'json': None,
                'url': url,
                'response_time': 0,
                'success': False,
                'error': str(e)
            }
            
    async def request_batch(self, requests: list, 
                           max_concurrent: int = 5,
                           batch_delay_range: tuple = (2.0, 5.0)) -> list:
        """
        批量发起请求
        
        Args:
            requests: 请求列表，每个请求是一个字典，包含method, url等参数
            max_concurrent: 最大并发数
            batch_delay_range: 批次间延迟范围
            
        Returns:
            响应结果列表
        """
        if not self.session:
            raise RuntimeError("请在async with语句中使用此方法")
            
        results = []
        
        # 分批处理请求
        for i in range(0, len(requests), max_concurrent):
            batch = requests[i:i + max_concurrent]
            
            if i > 0:
                # 批次间延迟
                delay = self._get_random_delay(*batch_delay_range)
                logger.info(f"批次间延迟 {delay:.2f}秒")
                await asyncio.sleep(delay)
            
            # 并发执行当前批次
            tasks = []
            for req in batch:
                method = req.get('method', 'GET').upper()
                if method == 'GET':
                    task = self.get(
                        req['url'],
                        params=req.get('params'),
                        headers=req.get('headers'),
                        token=req.get('token'),
                        delay_range=req.get('delay_range', (0.1, 0.5)),  # 批量请求使用较短延迟
                        timeout=req.get('timeout')
                    )
                elif method == 'POST':
                    task = self.post(
                        req['url'],
                        data=req.get('data'),
                        json_data=req.get('json_data'),
                        headers=req.get('headers'),
                        token=req.get('token'),
                        delay_range=req.get('delay_range', (0.1, 0.5)),  # 批量请求使用较短延迟
                        timeout=req.get('timeout')
                    )
                else:
                    continue
                    
                tasks.append(task)
            
            # 等待当前批次完成
            batch_results = await asyncio.gather(*tasks, return_exceptions=True)
            results.extend(batch_results)
            
            logger.info(f"完成批次 {i//max_concurrent + 1}/{(len(requests)-1)//max_concurrent + 1}")
        
        return results


# 使用示例
async def example_usage():
    """使用示例"""
    
    async with ChromeWebAPI(default_token="your-default-token") as api:
        
        # 单个GET请求
        response = await api.get(
            "https://httpbin.org/get",
            params={"param1": "value1"},
            headers={"Custom-Header": "custom-value"},
            delay_range=(1, 2)
        )
        print(f"GET响应: {response['status_code']}")
        
        # 单个POST请求
        response = await api.post(
            "https://httpbin.org/post",
            json_data={"key": "value"},
            headers={"Content-Type": "application/json"},
            delay_range=(2, 4)
        )
        print(f"POST响应: {response['status_code']}")
        
        # 批量请求
        requests = [
            {
                "method": "GET",
                "url": "https://httpbin.org/get",
                "params": {"id": 1}
            },
            {
                "method": "POST",
                "url": "https://httpbin.org/post",
                "json_data": {"id": 2}
            }
        ]
        
        results = await api.request_batch(requests, max_concurrent=2)
        print(f"批量请求完成，共 {len(results)} 个响应")


if __name__ == "__main__":
    # 运行示例
    print("示例开始")